use db::Db;

use anyhow::Result;
use tracing::{debug, info, warn, Level};
// use db::{s};
// use dotenv::dotenv;
use helpers::{Bot, Config};
use teloxide::{dispatching::UpdateFilterExt, prelude::*, types::Update};

mod handlers;
mod helpers;
mod keyboards;

#[tokio::main(flavor = "multi_thread")]
async fn main() {
    // Get values from .env file
    // dotenv().ok();
    run_bot().await;
}

use teloxide::utils::command::BotCommands;
#[derive(BotCommands, Debug)]
#[command(rename_rule = "lowercase", description = "description")]
enum Command {
    Start,
    #[command(description = "Send random post")]
    Random,
    #[command(description = "Count unread posts")]
    CountUnread,
    #[command(description = "count unread time")]
    CountUnreadTime,
    #[command(description = "Get all posts")]
    GetAllPosts,
    #[command(description = "display this text")]
    Help,
}
/// Parse what a command it is
async fn command_answer(
    bot: Bot,
    m: Message,
    command: Command,
    db: Db,
    config: Config,
) -> Result<()> {
    match command {
        Command::Help => {
            bot.send_message(m.chat.id, Command::descriptions().to_string())
                .await?;
            Ok(())
        }
        Command::Start => {
            handlers::start(bot, m, db, config).await?;
            Ok(())
        }
        Command::Random => {
            handlers::random(bot, m, db, config).await?;
            Ok(())
        }
        Command::CountUnread => {
            handlers::count_unread_posts(bot, m, db, config).await?;
            Ok(())
        }
        Command::CountUnreadTime => {
            //handlers::count_unread_time(bot, m, db, config).await?;
            Ok(())
        }
        Command::GetAllPosts => {
            // handlers::get_all_posts(bot, m, db, config).await?;
            Ok(())
        }
    }
}
/// If message is a command - parse like a command. Else - it is a url to save
pub async fn messages_handler(m: Message, bot: Bot, db: Db, config: Config) -> Result<()> {
    info!("New message");
    if let Some(text) = parse_text(&m) {
        if let Ok(command) = BotCommands::parse(text, "readlaterbot") {
            command_answer(bot, m, command, db, config).await?;
        } else {
            handlers::add(bot, m, db, config).await?;
        }
    }
    Ok(())
}
fn parse_text(m: &Message) -> Option<&str> {
    let mut res = None;
    if let Some(text) = m.text() {
        res = Some(text);
    }
    if let Some(text) = m.caption() {
        res = Some(text);
    }
    res
}

async fn handle_callback_query(q: CallbackQuery, bot: Bot, db: Db, config: Config) -> Result<()> {
    info!("New callback");
    let data = q.data.clone();
    match data {
        None => {}
        Some(data) => {
            // TODO: ref using enums
            let data = data.split_whitespace().collect::<Vec<&str>>();
            let command = data.first().unwrap();
            let index = data.get(1).unwrap();
            match *command {
                "del" => {
                    crate::handlers::delete(bot, q, index, db, config).await?;
                }
                "archive" => {
                    crate::handlers::archive(bot, q, index, db, config).await?;
                }
                "unarchive" => {
                    crate::handlers::archive(bot, q, index, db, config).await?;
                }
                "page" => {
                    // crate::handlers::change_page(bot, q, index, db, config).await?;
                }
                "post" => {
                    // crate::handlers::get_post_info(bot, q, index, db, config).await?;
                }
                "favorite" => {
                    crate::handlers::favorite(bot, q, index, db, config).await?;
                }
                "unfavorite" => {
                    crate::handlers::unfavorite(bot, q, index, db, config).await?;
                }
                "real_url" => {
                    crate::handlers::real_url(bot, q, index, db, config).await?;
                }
                "original_url" => {
                    crate::handlers::original_url(bot, q, index, db, config).await?;
                }
                "rerender" => {
//                    crate::handlers::rerender(bot, q, index, db, config).await?;
                }
                _ => {}
            }
        }
    }
    Ok(())
}
use tracing_subscriber::FmtSubscriber;

async fn run_bot() {
    // a builder for `FmtSubscriber`.
    let subscriber = FmtSubscriber::builder()
        // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.)
        // will be written to stdout.
        .with_max_level(Level::INFO)
        //    .with_target(display_target)
        // completes the builder.
        .finish();
    tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
    // console_subscriber::init();
    let file_path = std::env::var("CONFIG_FILE").unwrap();
    let config = helpers::Config::load(file_path).await.unwrap();
    debug!("config: {config:?}");
    let db = db::Db::connect(&config.postgres_url).await.unwrap();
    info!("Starting readlaterbot...");
    use teloxide::{adaptors::throttle::Limits, requests::RequesterExt, types::ParseMode};
    let bot = teloxide::Bot::new(&config.bot_token)
        .parse_mode(ParseMode::MarkdownV2)
        .throttle(Limits::default()); //.unwrap();
    let handler = dptree::entry()
        //.branch(Update::filter_message().filter_command::<Command>().endpoint(command_answer))
        .branch(Update::filter_message().endpoint(messages_handler))
        .branch(Update::filter_callback_query().endpoint(handle_callback_query));
    Dispatcher::builder(bot, handler)
        .dependencies(dptree::deps![db.clone(), config.clone()])
        .default_handler(|upd| async move {
            warn!("Unhandled update: {:?}", upd);
        })
        // If the dispatcher fails for some reason, execute this handler.
        /*        .error_handler(LoggingErrorHandler::with_custom_text(
            "An error has occurred in the dispatcher",
        ))*/
        .enable_ctrlc_handler()
        .distribution_function(|_| None::<std::convert::Infallible>)
        .build()
        .dispatch()
        .await;
}
